<?xml version="1.0" encoding="UTF-8"?><d:tdl xmlns="http://www.w3.org/1999/xhtml" xmlns:b="http://www.backbase.com/2006/btl" xmlns:bb="http://www.backbase.com/2006/client"  xmlns:d="http://www.backbase.com/2006/tdl" >

	<d:namespace name="http://www.backbase.com/2006/btl">

		<d:uses element="formList formListOption" src="../formList/formList.xml"/>
		<d:uses element="positionElement dimensionElement" src="../visualElement/visualElement.xml"/>
		<d:uses element="focusableElement" src="../focus/focus.xml"/>

		<d:element name="listBoxBase" extends="b:formList b:positionElement b:dimensionElement b:focusableElement" abstract="true">
			

			<d:attribute name="multiple" default="false">
				
			</d:attribute>

			<d:attribute name="size" default="5">
				
			</d:attribute>

			<!-- required to submit this control -->
			<d:property name="multiple" onget="return btl.isTrueValue(name, this.getAttribute(name))">
				
			</d:property>

			<d:property name="focusIndex">
				
				<d:setter type="text/javascript"><![CDATA[
					// Remove focus indicator from old option (if it exists) and add focus indicator to new option (if it exists)
					var aOptions = this.getProperty('options');
					var iOldValue = this._['_focusIndex'];

					if(!(value > -1 && aOptions.length > value)){
						var iSelectedIndex = this.getProperty('selectedIndex');
						if(iSelectedIndex == -1)
							value = 0;
						else
							value = iSelectedIndex;
					}

					if(value !== iOldValue){
						if(aOptions[value]){
							if(this.__oldFocus && this.__oldFocus._)
								this.__oldFocus.hideFocus();

							aOptions[value].showFocus();
							//scroll into view
							var oViewNode = aOptions[value].viewNode;
							var iDelta = this.__scrollBox.scrollTop - oViewNode.offsetTop;
							if( iDelta > 0)
								this.__scrollBox.scrollTop -= iDelta;
							else {
								iDelta = (oViewNode.offsetTop + oViewNode.offsetHeight) - (this.__scrollBox.offsetHeight + this.__scrollBox.scrollTop);
								if( iDelta > 0)
									this.__scrollBox.scrollTop += iDelta;
							}
							this.__oldFocus = aOptions[value];
						}
					}

					this._['_focusIndex'] = value;
				]]></d:setter>
			</d:property>

			<d:property name="selectedIndex">
				
				<d:setter type="text/javascript"><![CDATA[
					// Setting selectedIndex removes old selection and selects new option if one exists at that index.
					// Valid values: -1 (nothing selected) to options.length -1 (the last option)
					// Values that are out of range become -1

					// Set up some variables
					var aOptions = this.getProperty('options');
					var iOldValue = this.getProperty('selectedIndex');
					if(value < -1 || value >= aOptions.length) value = -1;

					// Remove old selection
					for(var i = 0, iLimit = aOptions.length; i < iLimit; i++){
						if(i != value) {
							aOptions[i].setProperty('selected', false);
						}
					}

					// Select new option
					if(value != -1 && value != iOldValue){
						aOptions[value].setProperty('selected', true);
						this.setProperty('focusIndex', value);
					}
				]]></d:setter>
				<!-- REMOVE THE GETTER WHEN BUG 6509 IS FIXED -->
				<d:getter type="text/javascript"><![CDATA[
					// by default, the index is -1, unless an option is selected;
					var iSelectedIndex = -1;
					var aOptions = this.getProperty('options');
					for(var i = 0, iMax = aOptions.length; iMax > i; i++){
						if(aOptions[i].getProperty('selected')){
							iSelectedIndex = i;
							break
						}
					}
					return iSelectedIndex;
				]]></d:getter>
			</d:property>

			<d:property name="selectedIndexes">
				
				<d:getter type="text/javascript"><![CDATA[
					var aOptions = this.getProperty('options');
					var aSelected = [];
					for (var iOption = 0; iOption < aOptions.length; ++iOption) {
						if (aOptions[iOption].getProperty("selected")) {
							aSelected.push(iOption);
						}
					}

					return aSelected;
				]]></d:getter>
			</d:property>

			<d:handler event="mousedown" type="text/javascript"><![CDATA[
				var scrollTop = this.__scrollBox.scrollTop;
				this.focus();
				if (event.button == 0) {
					event.preventDefault();
					if (bb.selector.match(event.viewTarget, '.btl-listBoxOption-td')) {
						this.__scrollBox.scrollTop = scrollTop;
						var bMultiple = this.getProperty('multiple');
						var bCtrl = !bb.browser.opera && navigator.platform.indexOf('Mac') != -1 ? event.metaKey : event.ctrlKey;
						var bFire = true;

						var aOptions = this.getProperty('options');
						var iTIndex = 0;
						for(var i = 0, iMax = aOptions.length; iMax > i; i++){
							iTIndex = i;
							if(aOptions[i] == event.target){
								break;
							}
						}

						var bSelected = event.target.getProperty('selected');

						if (bMultiple && bCtrl) {
							event.target.setProperty('selected', bSelected ? false : true);

							this.__selStart = iTIndex;

						} else if (bMultiple && event.shiftKey){
							var ind1 = isNaN(this.__selStart) || 0 > this.__selStart || this.__selStart >= aOptions.length ? iTIndex : this.__selStart;
							var ind2 = iTIndex;
							if (ind2 < ind1){
								ind2 = ind1;
								ind1 = iTIndex;
							}

							for (var i = aOptions.length-1; i >= 0; i--)
								aOptions[i].setProperty('selected', (i >= ind1 && ind2 >= i));

						} else {
							// Regular mousedown deselects old options and selects new option
							var bSelDrop = false;
							for (var i = 0, iMax = aOptions.length; iMax > i; i++) {
								if(event.target != aOptions[i]){
									bSelDrop |= aOptions[i].getProperty('selected');
									aOptions[i].setProperty('selected', false);
								}
							}

							if(bSelected && bCtrl){
								event.target.setProperty('selected', false);
							} else if (!bSelected){
								event.target.setProperty('selected', true);
							}else if(!bSelDrop){
								bFire = false;
							}
							this.__selStart = iTIndex;
						}
						if( bFire)  // dispatch change event
							bb.command.fireEvent( this, 'change', true, false);

						this.setProperty('focusIndex', iTIndex);
					}
				}
			]]></d:handler>

			<d:handler event="keydown" type="text/javascript"><![CDATA[
				// Up or Down pressed
				var oNavKeys = {'Up' : true, 'Down' : true, 'Home' : true, 'End' : true, 'PageUp' : true, 'PageDown' : true};
				var bCtrl = navigator.platform.indexOf('Mac') != -1 ? event.metaKey || event.ctrlKey : event.ctrlKey;
				var aSelOptions = [];
				var aOptions = this.getProperty('options');
				if( event.keyIdentifier in oNavKeys){

					var iFocusIndex = this.getProperty('focusIndex');
					var iFocusIndexOld = iFocusIndex;

					var selectedIndex = this.getProperty('selectedIndex');
					switch( event.keyIdentifier){
						case 'Up': iFocusIndex--; break;
						case 'Down': iFocusIndex++; break;
						case 'Home': iFocusIndex=0; break;
						case 'End': iFocusIndex=aOptions.length-1; break;
						case 'PageUp': //put on bottom the focused row or the top one
							if( aOptions[iFocusIndexOld]){
								var oViewNode = aOptions[iFocusIndexOld].viewNode;
								var iBottom = this.__scrollBox.offsetHeight + this.__scrollBox.scrollTop - oViewNode.offsetHeight;
								if( iBottom > oViewNode.offsetTop){//put the focused on bottom
									this.__scrollBox.scrollTop = oViewNode.offsetTop + oViewNode.offsetHeight - this.__scrollBox.offsetHeight;
								}
								//focus the top
								iFocusIndex = iFocusIndexOld;
								for(var i=iFocusIndexOld; i >= 0; i--){
									if( aOptions[i].viewNode.offsetTop < this.__scrollBox.scrollTop) break;
									iFocusIndex = i;
								}
								//adjust slightly
								if(iFocusIndex != iFocusIndexOld)
									this.__scrollBox.scrollTop = aOptions[iFocusIndex].viewNode.offsetTop;
							}
							break;
						case 'PageDown': //put on top the focused row or the bottom one
							if( aOptions[iFocusIndexOld]){
								var oViewNode = aOptions[iFocusIndexOld].viewNode;
								if( this.__scrollBox.scrollTop + oViewNode.offsetHeight < oViewNode.offsetTop){//put the focused on top
									this.__scrollBox.scrollTop = oViewNode.offsetTop;
								}
								//focus the bottom
								var iBottom = this.__scrollBox.offsetHeight + this.__scrollBox.scrollTop - oViewNode.offsetHeight;
								iFocusIndex = iFocusIndexOld;
								for(var i=iFocusIndexOld, iMax=aOptions.length; iMax > i; i++){
									if( aOptions[i].viewNode.offsetTop > iBottom) break;
									iFocusIndex = i;
								}
							}
							break;
					}
					// only change selection/focusIndex when the new value is in range
					var bInRange = true;
					if(iFocusIndex < 0 || iFocusIndex >= aOptions.length){
						iFocusIndex = iFocusIndexOld;
						bInRange = false;
					}

					if(bInRange){
						if (bCtrl){
							// Ctrl pressed -> only move the focus
							this.setProperty('focusIndex', iFocusIndex);
							this.__selStart = iFocusIndex;
						} else if (event.shiftKey && this.getProperty('multiple')) {
							//SHIFT pressed ->
							aSelOptions = this.getProperty("selectedIndexes");
							// for the first time?
							if (typeof this.__shiftAnchor != "number") {
								// deselecting everything that was selected before shift was pressed, except current
								for (
									var i = 0, iIndex = aSelOptions[i];
									i < aSelOptions.length;
									++i, iIndex = aSelOptions[i]
								) {
									if (iIndex != iFocusIndexOld) {
										aOptions[iIndex].setProperty("selected", false);
									}
								}
								// saving the index of the option used as an anchor of selection using shift
								this.__shiftAnchor = iFocusIndexOld;
								// selecting current option if it was not still
								aOptions[iFocusIndexOld].setProperty("selected", true);
							} else if ( iFocusIndexOld > iFocusIndex && iFocusIndexOld > this.__shiftAnchor){
								// if this was move backward we need to deselect previous option
								for(var i=iFocusIndexOld, iMin = Math.max(this.__shiftAnchor, iFocusIndex); i > iMin; i--)
									aOptions[i].setProperty('selected', false);
							} else if ( iFocusIndexOld < iFocusIndex && iFocusIndexOld < this.__shiftAnchor){
								// if this was move backward we need to deselect previous option
								for(var i=iFocusIndexOld, iMax = Math.min(this.__shiftAnchor, iFocusIndex); i < iMax; i++)
									aOptions[i].setProperty('selected', false);
							}
							//set selection
							if(this.__shiftAnchor > iFocusIndex){
								for(var i=this.__shiftAnchor-1; i >= iFocusIndex; i--)
									aOptions[i].setProperty('selected', true);
							}else{
								for(var i=this.__shiftAnchor+1; i <= iFocusIndex; i++)
									aOptions[i].setProperty('selected', true);
							}
							//set focus index
							this.setProperty('focusIndex', iFocusIndex);

							// dispatch change event
							bb.command.fireEvent( this, 'change', true, false);
						}
						else {
							this.__selStart = iFocusIndex;
							// Plain Up or Down removes older selections and selects focused option
							if(!aOptions[iFocusIndex].getProperty('selected') || this.getProperty('multiple')){
								this.setProperty('selectedIndex', iFocusIndex);
								// dispatch change event
								bb.command.fireEvent( this, 'change', true, false);
							}
						}
					}

					//prevent scrollbars from moving
					event.preventDefault();
				//SPACE pressed
				} else if (event.keyIdentifier == 'U+0020'){
					var iFocusIndex = this.getProperty('focusIndex');
					this.__selStart = iFocusIndex;

					// if CTRL is pressed, the selection should be toggled
					if (bCtrl){
						if(aOptions[iFocusIndex].getProperty('selected'))
							if(this.getProperty('multiple'))
								aOptions[iFocusIndex].setProperty('selected', false);
							else
								this.setProperty('selectedIndex', -1);
						else {
							if(this.getProperty('multiple'))
								aOptions[iFocusIndex].setProperty('selected', true);
							else
								this.setProperty('selectedIndex', iFocusIndex);
						}

						// dispatch change event
						bb.command.fireEvent( this, 'change', true, false);
					// otherwise it should just select it
					} else {
						if(!aOptions[iFocusIndex].getProperty('selected')){
							this.setProperty('selectedIndex', iFocusIndex);
							// dispatch change event
							bb.command.fireEvent( this, 'change', true, false);
						}
					}



					/* When space is pressed, the control needs to be blurred before it
						responds again to key input. To avoid this, prevent the default
						behavior that causes this.
					*/
					event.preventDefault();
				}
				// need to clear the __shiftAnchor if this was keydown without SHIFT key
				if (!event.shiftKey) {
					this.__shiftAnchor = null;
				}
			]]></d:handler>
			<d:handler event="DOMNodeRemoved" type="text/javascript"><![CDATA[
				if (bb.instanceOf(event.target, btl.namespaceURI, "listBoxOptionBase")){
					//Remove selection if moved out.
					if (event.target.getProperty('selected'))
						event.target.setProperty('selected', false)
					event.target.hideFocus();
				}
			]]></d:handler>
			<d:handler event="focus" type="text/javascript"><![CDATA[
				this.setProperty('focusIndex', this.getProperty('focusIndex'));
			]]></d:handler>
		</d:element>

		<d:element name="listBoxOptionBase" extends="b:formListOption" abstract="true">
			
		</d:element>
	</d:namespace>
</d:tdl>